﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Collections.ObjectModel;
using System.IO;
using System.Windows.Browser;

namespace DC.FileUpload
{
    public partial class FileUploadControl : UserControl
    {
        public event RoutedEventHandler UploadComplete;

        public int MaxConcurrentUploads { get; set; }
        public long UploadChunkSize { get; set; }
        public bool ResizeImage { get; set; }
        public int ImageSize { get; set; }
        private DateTime start;

        public string Filter { get; set; }
        public bool Multiselect { get; set; }
        public Uri UploadUrl { get; set; }

        private bool uploading { get; set; }

        private long TotalUploadSize { get; set; }
        private long TotalUploaded { get; set; }

        public long MaximumTotalUpload { get; set; }
        public long MaximumUpload { get; set; }

        public int MaxNumberToUpload { get; set; }

        public ObservableCollection<FileUpload> files;

        public FileUploadControl()
        {
            InitializeComponent();
            files = new ObservableCollection<FileUpload>();
            files.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(files_CollectionChanged);
            MaxConcurrentUploads = 1;
            MaxNumberToUpload = 1;
            MaximumTotalUpload = MaximumUpload = -1;
            Filter = ".Mp3 Files|*.mp3";
            Multiselect = true;
            uploading = false;
            UploadChunkSize = 4194304;

            addFilesButton.Click += new RoutedEventHandler(addFilesButton_Click);
            uploadButton.Click += new RoutedEventHandler(uploadButton_Click);
            clearFilesButton.Click += new RoutedEventHandler(clearFilesButton_Click);

            Loaded += new RoutedEventHandler(FileUploadControl_Loaded);
        }

        public FileUploadControl(Uri uploadUrl)
            : this()
        {
            UploadUrl = uploadUrl;
        }

        void files_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            TotalUploadSize = files.Sum(f => f.FileLength);
            TotalUploaded = files.Sum(f => f.BytesUploaded);
            progressBar.Maximum = TotalUploadSize;
            progressBar.Value = TotalUploaded;
        }

        public void SetToSelectFileMode()
        {
            // Hide Upload button
            uploadButton.Visibility = Visibility.Collapsed;

            // Show addFilesButton
            addFilesButton.Visibility = Visibility.Visible;
        }

        void clearFilesButton_Click(object sender, RoutedEventArgs e)
        {
            // Hide Upload button
            uploadButton.Visibility = Visibility.Collapsed;

            // Show addFilesButton
            addFilesButton.Visibility = Visibility.Visible;

            var q = files.Where(f => f.Status == FileUploadStatus.Uploading);
            foreach (FileUpload fu in files)
            {
                fu.CancelUpload();
            }

            timeLeftTextBlock.Text = "";
            files.Clear();
        }

        void uploadButton_Click(object sender, RoutedEventArgs e)
        {
            if ((string)uploadButton.Content == "Upload")
            {
                uploadButton.Content = "Cancel";
                start = DateTime.Now;
                UploadFiles();
            }
            else
            {
                var q = files.Where(f => f.Status == FileUploadStatus.Uploading);
                foreach (FileUpload fu in q)
                    fu.CancelUpload();
                uploading = false;
                uploadButton.Content = "Upload";
            }
        }

        void addFilesButton_Click(object sender, RoutedEventArgs e)
        {
            OpenFileDialog dlg = new OpenFileDialog();
            dlg.Filter = Filter;
            dlg.Multiselect = false;

            if ((bool)dlg.ShowDialog())
            {
                // Show Upload button
                uploadButton.Visibility = Visibility.Visible;

                // Hide addFilesButton
                addFilesButton.Visibility = Visibility.Collapsed;

                foreach (FileInfo file in dlg.Files)
                {
                    FileUpload upload = new FileUpload(this.Dispatcher, UploadUrl, file);
                    if (UploadChunkSize > 0)
                    {
                        upload.ChunkSize = UploadChunkSize;
                    }

                    if (MaximumTotalUpload >= 0 && TotalUploadSize + upload.FileLength > MaximumTotalUpload)
                    {
                        MessageBox.Show("You have exceeded the total allowable upload amount.");
                        break;
                    }

                    if (MaximumUpload >= 0 && upload.FileLength > MaximumUpload)
                    {
                        MessageBox.Show(string.Format("The file '{0}' exceeds the maximun upload size.", upload.Name));
                        continue;
                    }
                    upload.StatusChanged += new EventHandler(upload_StatusChanged);
                    upload.UploadProgressChanged += new ProgressChangedEvent(upload_UploadProgressChanged);
                    upload.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(upload_PropertyChanged);
                    files.Add(upload);
                }
            }
        }

        void upload_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "FileLength")
            {
                files_CollectionChanged(null, null);
            }
        }

        void upload_UploadProgressChanged(object sender, UploadProgressChangedEventArgs args)
        {
            TotalUploaded += args.BytesUploaded;
            progressBar.Value = TotalUploaded;

            double ByteProcessTime = 0;
            double EstimatedTime = 0;

            try
            {
                TimeSpan timeSpan = DateTime.Now - start;
                ByteProcessTime = TotalUploaded / timeSpan.TotalSeconds;
                EstimatedTime = (TotalUploadSize - TotalUploaded) / ByteProcessTime;
                timeSpan = TimeSpan.FromSeconds(EstimatedTime);
                timeLeftTextBlock.Text = string.Format("{0:00}:{1:00}:{2:00}", timeSpan.Hours, timeSpan.Minutes, timeSpan.Seconds);
            }
            catch { }
        }

        void upload_StatusChanged(object sender, EventArgs e)
        {
            FileUpload fu = sender as FileUpload;
            if (fu.Status == FileUploadStatus.Complete)
            {
                if (uploading)
                    UploadFiles();
            }
            else if (fu.Status == FileUploadStatus.Removed)
            {
                files.Remove(fu);
                if (uploading)
                    UploadFiles();
            }
            else if (fu.Status == FileUploadStatus.Uploading && files.Count(f => f.Status == FileUploadStatus.Uploading) < MaxConcurrentUploads)
                UploadFiles();
        }

        void FileUploadControl_Loaded(object sender, RoutedEventArgs e)
        {
            fileList.ItemsSource = files;
        }

        private void UploadFiles()
        {
            uploading = true;
            while (files.Count(f => f.Status == FileUploadStatus.Uploading || f.Status == FileUploadStatus.Resizing) < MaxConcurrentUploads && uploading)
            {
                if (files.Count(f => f.Status != FileUploadStatus.Complete && f.Status != FileUploadStatus.Uploading && f.Status != FileUploadStatus.Resizing) > 0)
                {
                    FileUpload fu = files.First(f => f.Status != FileUploadStatus.Complete && f.Status != FileUploadStatus.Uploading && f.Status != FileUploadStatus.Resizing);
                    fu.Upload();
                }
                else if (files.Count(f => f.Status == FileUploadStatus.Uploading || f.Status == FileUploadStatus.Resizing) == 0)
                {
                    uploading = false;
                    uploadButton.Content = "Upload";

                    // Raise completed event
                    if (UploadComplete != null)
                    {
                        UploadComplete(this, new RoutedEventArgs());
                    }
                }
                else
                {
                    break;
                }
            }
        }
    }
}
